﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Reflection.Emit;
using System.Reflection;
using System.Diagnostics;

namespace ORMAC.UnitTest.Reflection
{
    public delegate void SetPropertyFieldDelegate(object obj, string m_Value);

    [TestFixture]
    public class ReflectionTester
    {
        class TestObject{
            private string m_Str;
            private int m_ID;
            public int ID{
                get{
                    return m_ID;
                }
                set{
                    m_ID=value;
                }
            }
            public string Str
            {
                get
                {
                    return m_Str;
                }
                set
                {
                    m_Str = value;
                }
            }
        }
        [Test]
        public void TestSetPerformance(){
            TestObject obj=new TestObject();
            PropertyInfo pInfo= typeof(TestObject).GetProperty("Str");
            Stopwatch s = new Stopwatch();
            s.Start();
            pInfo.SetValue(obj,"rtert", null);
            s.Stop();
            Console.WriteLine("Set operation took " + s.ElapsedTicks);
            s.Start();
            obj.Str= "rtert";
            s.Stop();
            Console.WriteLine("Assignment operation took " + s.ElapsedTicks);
        }
        [Test]
        public void TestEmitPerformance()
        {
            TestObject obj = new TestObject();
            PropertyInfo pInfo = typeof(TestObject).GetProperty("Str");
            Stopwatch s = new Stopwatch();
            Action<object, object> setter = MakeSetter(pInfo);
            s.Start();
            setter(obj,"werewrewr");            
            s.Stop();
            Console.WriteLine("Set(Emit) operation took " + s.ElapsedTicks);
        }
        [Test]
        public void TestEmitPerformance2()
        {
            TestObject obj = new TestObject();
            FieldInfo pInfo = typeof(TestObject).GetField("m_Str", BindingFlags.Instance | BindingFlags.NonPublic);
            Stopwatch s = new Stopwatch();
            SetPropertyFieldDelegate setter = MakeSetter(pInfo);
            s.Start();
            setter(obj, "fgfdg");
            s.Stop();
            Console.WriteLine("Set(Emit) operation took " + s.ElapsedTicks);
        }
        static SetPropertyFieldDelegate MakeSetter(FieldInfo field)
        {
            DynamicMethod dm = new DynamicMethod("SetPropertyorField_" + field.Name, typeof(void),
                               new Type[] { typeof(object), typeof(string) }, field.Module, true);
            ILGenerator cg = dm.GetILGenerator();
            // arg0.<field> = arg1
            cg.Emit(OpCodes.Ldarg_0);
            cg.Emit(OpCodes.Ldarg_1);
            cg.Emit(OpCodes.Stfld, field);
            if (field.FieldType.IsValueType)
            {
                cg.Emit(OpCodes.Unbox_Any, field.FieldType);
            }
            cg.Emit(OpCodes.Ret);
            return (SetPropertyFieldDelegate)dm.CreateDelegate(typeof(SetPropertyFieldDelegate));
        }

        
        static Action<object, object> MakeSetter(PropertyInfo field)
        {
            DynamicMethod dm = new DynamicMethod("SetPropertyorField_" + field.Name, typeof(void),
                               new Type[] { typeof(object), typeof(object) }, field.Module, true);
            ILGenerator cg = dm.GetILGenerator();
            // arg0.<field> = arg1
            cg.Emit(OpCodes.Ldarg_0);
            cg.Emit(OpCodes.Ldarg_1);
            cg.EmitCall(OpCodes.Call, field.GetSetMethod(), null);
            if (field.PropertyType.IsValueType)
            {
                cg.Emit(OpCodes.Unbox_Any, field.PropertyType);
            }
            cg.Emit(OpCodes.Ret);
            return (Action<object, object>)dm.CreateDelegate(typeof(Action<object, object>));
        }

    }
}
